﻿#include"XQTimeDoubleDataModel.h"
XQTimeDoubleDataModel::XQTimeDoubleDataModel(QObject* parent)
{
}

XQTimeDoubleDataModel::~XQTimeDoubleDataModel()
{
}

bool XQTimeDoubleDataModel::getStartEndTime(int type, const QList<QDateTime>& dataTimes, const QDateTime& start, const QDateTime& end, QDateTime* startOut, QDateTime* endOut)
{
	if (dataTimes.isEmpty() || start.msecsTo(end) <= 0 ||
		start < QDateTime(QDate(1900, 1, 1), QTime(0, 0, 0, 0)) ||
		end < QDateTime(QDate(1900, 1, 1), QTime(0, 0, 0, 0)))
		return false;
	QDateTime startCur, endCur;
	auto minDate = dataTimes.front();
	auto maxDate = dataTimes.back();
	//auto min = minDate.toString("yyyy-MM-dd hh:mm:ss");
	//auto max = maxDate.toString("yyyy-MM-dd hh:mm:ss");
	qint64 days = 0;
	QDateTime startBig, startSmall = maxDate, endBig, endSmall = maxDate;
	while (true)
	{
		if ((type & getStartEndTime_max))
		{
			if (startBig <= maxDate)
			{
				startBig = start.addDays(days);
				if (startCur.isNull() && dataTimes.contains(startBig))
					startCur = startBig;
			}
		}
		if ((type & getStartEndTime_min))
		{
			if (startSmall >= minDate)
			{
				startSmall = start.addDays(-days);
				if (startCur.isNull() && dataTimes.contains(startSmall))
					startCur = startSmall;
			}
		}
		if ((type & getStartEndTime_max))
		{
			if (endBig <= maxDate)
			{
				endBig = end.addDays(days);
				if (endCur.isNull() && dataTimes.contains(endBig))
					endCur = endBig;
			}
		}
		if ((type & getStartEndTime_min))
		{
			if (endSmall >= minDate)
			{
				endSmall = end.addDays(-days);
				if (endCur.isNull() && dataTimes.contains(endSmall))
					endCur = endSmall;
			}
		}
		if ((type & (getStartEndTime_max | getStartEndTime_min)) && endBig > maxDate && startBig > maxDate && startSmall < minDate && endSmall < minDate)
			break;
		else if ((type & getStartEndTime_max) && endBig > maxDate && startBig > maxDate)
			break;
		else if ((type & getStartEndTime_min) && startSmall < minDate && endSmall < minDate)
			break;
		if (!startCur.isNull() && !endCur.isNull())
			break;
		/*if (days > data->size())
			break;*/
		++days;
	}
	if (startCur.isNull() || endCur.isNull())//最后还是没符合的
		return false;
	if (startOut != nullptr)
		*startOut = startCur;
	if (endOut != nullptr)
		*endOut = endCur;
	return true;
}

bool XQTimeDoubleDataModel::getStartEndTime(const QDateTime& start, const QDateTime& end, QDateTime* startOut, QDateTime* endOut)
{
	return getStartEndTime(getStartEndTime_all,m_data.keys(),start,end,startOut,endOut);
}

double XQTimeDoubleDataModel::changePercent(int type, const QMap<QDateTime, double>& datas, const QDateTime& start, const QDateTime& end, QDateTime* startOut, QDateTime* endOut)
{
	if (datas.isEmpty() || start.msecsTo(end) <= 0)
		return 0.0;
	QDateTime startCur, endCur;
	if (!getStartEndTime(type,datas.keys(), start, end, &startCur, &endCur))//最后还是没符合的
		return 0.0;
	if (startOut != nullptr)
		*startOut = startCur;
	if (endOut != nullptr)
		*endOut = endCur;
	auto startValue = (datas)[startCur];
	auto endValue = (datas)[endCur];
	if (startValue == 0.0)
		startValue = 1;
	return endValue / startValue - 1;
}

double XQTimeDoubleDataModel::changePercent(const QDateTime& start, const QDateTime& end, QDateTime* startOut, QDateTime* endOut)const
{
	return changePercent(getStartEndTime_max, m_data, start, end, startOut, endOut);
}

XQHistoryTrend XQTimeDoubleDataModel::getHistoryTrend() const
{
	auto dates = datas().keys();//所有的日期
	if (dates.isEmpty())//是空的
		return XQHistoryTrend();
	//设置趋势的项
	auto setTrend = [=](XQHistoryTrendItem* item, const QDateTime& start, const QDateTime& end)
	{
		if (item == nullptr)
			return;
		QDateTime startOut, endOut;
		auto value = changePercent(start, end, &startOut, &endOut);
		if (startOut.isNull() || endOut.isNull())
			return;//失败了
		item->startTime = startOut;
		item->endTime = endOut;
		item->value = value;
	};
	XQHistoryTrend trend;
	QDateTime startDate = dates.front();//已有的最近时间
	QDateTime endDate = dates.back();//已有的最近时间
	QMap<qint64, XQHistoryTrendItem*> itemMap;//项映射
	{
		itemMap[7] = &trend.OneWeek;
		itemMap[30] = &trend.OneMonth;
		itemMap[90] = &trend.ThreeMonth;
		itemMap[180] = &trend.SixMonth;
		itemMap[365] = &trend.OneYear;
		itemMap[730] = &trend.TwoYear;
		itemMap[1095] = &trend.ThreeYear;
		itemMap[1825] = &trend.FiveYear;
		itemMap[QDate(endDate.date().year(), 1, 1).daysTo(endDate.date())] = &trend.ThisYear;
	}
	std::reverse(dates.begin(), dates.end());
	qint64 lastdayCount = 0;//记录上一次的天数
	QDateTime lastDate;//上次的日期
	auto times = itemMap.keys();//获取天数合集
	auto itTime = times.begin();//我需要的天数
	for (auto& date : dates)
	{
		if (itTime == times.end())
			break;
		auto curdate = date;
		qint64 days = curdate.daysTo(endDate);
		if (days == *itTime)
		{
			setTrend(itemMap[*itTime], date, endDate);
			//qInfo() << date<< "添加" + QString::number(*itTime) + "-" + QString::number(days);
			itTime++;
		}
		else if (days > *itTime)
		{
			int lastdiff = abs(lastdayCount - *itTime);//上次的差值
			int curdiff = abs(days - *itTime);//当前的差值
			if (lastdiff < curdiff)//选小的
			{
				setTrend(itemMap[*itTime], lastDate, endDate);
				//qInfo() << date << "添加" + QString::number(*itTime) + "-" + QString::number(lastdayCount);
			}
			else
			{
				setTrend(itemMap[*itTime], date, endDate);
				//qInfo() << date << "添加" + QString::number(*itTime) + "-" + QString::number(days);
			}
			itTime++;
		}
		lastdayCount = days;
		lastDate = date;
	}
	//成立至今
	setTrend(&trend.Hitherto, startDate, endDate);
	trend.isNull = false;
	/*trend.code = code;*/
	return std::move(trend);
}

const QMap<QDateTime, double>& XQTimeDoubleDataModel::datas() const
{
	return m_data;
}

double XQTimeDoubleDataModel::data(QDateTime time) const
{
	if (m_data.find(time) == m_data.end())
		return 0.0;
	return m_data[time];
}

QPair<double, double> XQTimeDoubleDataModel::value_min_max(const QDateTime& startDate, const QDateTime& endDate)
{
	if(startDate.isNull()|| endDate.isNull()|| startDate> endDate)
		return QPair<double, double>();
	QDateTime start, end;
	if(!getStartEndTime(startDate, endDate, &start, &end))
		return QPair<double, double>();
	auto itEnd = ++m_data.find(endDate);
	double max = -INT64_MAX, min = INT64_MAX;
	for (auto it=m_data.find(start);it!=m_data.end()&&it!= itEnd;it++)
	{
		max = qMax(max,it.value());
		min = qMin(min, it.value());
	}
	return QPair<double, double>(min,max);
}

double XQTimeDoubleDataModel::value_max() const
{
	return m_max;
}

double XQTimeDoubleDataModel::value_min() const
{
	return m_min;
}

QDateTime XQTimeDoubleDataModel::time_max() const
{
	if (m_data.isEmpty())
		return QDateTime();
	return (--m_data.end()).key();
	
}

QDateTime XQTimeDoubleDataModel::time_min() const
{
	if (m_data.isEmpty())
		return QDateTime();
	return m_data.begin().key();
}

void XQTimeDoubleDataModel::insert(QDateTime time, double value)
{
	m_data[time] = value;
	m_min = qMin(m_min, value);
	m_max = qMax(m_max, value);
}

void XQTimeDoubleDataModel::remove(QDateTime time)
{
	if(m_data.find(time) == m_data.end())
		return;
	auto value = m_data[time];
	m_data.remove(time);
	if (qFuzzyCompare(value, m_max) || qFuzzyCompare(value, m_min))
	{
		updateMaxMin();
	}
}

void XQTimeDoubleDataModel::clear()
{
	m_data.clear();
	m_max = -INT_FAST64_MAX;
	m_min= INT_FAST64_MAX;
}

void XQTimeDoubleDataModel::updateMaxMin()
{
	m_max = -INT_FAST64_MAX; m_min = INT_FAST64_MAX;
	for (auto& num : m_data)
	{
		m_min = qMin(m_min, num);
		m_max = qMax(m_max, num);
	}
}

bool XQHistoryTrendItem::isEmpty() const
{
	return startTime.isNull()||endTime.isNull();
}

QString XQHistoryTrendItem::startToString(const QString& format, const QString& defaultValue) const
{
	QString str = std::move(startTime.toString(format));
	if (str.isEmpty())
		return defaultValue;
	return std::move(str);
}

QString XQHistoryTrendItem::endToString(const QString& format, const QString& defaultValue) const
{
	QString str = std::move(endTime.toString(format));
	if (str.isEmpty())
		return defaultValue;
	return std::move(str);
}

QString XQHistoryTrendItem::toString() const
{
	return QString("%1%  %2/%3").arg(QString::number(value * 100, 'f', 2).rightJustified(6, ' ')).arg(startToString()).arg(endToString());
}
